// (c) Copyright Romanian Software Company.
// Last Change: 10.02.2010 - by Ionescu Marius.
// All rights reserved.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace Cryptography
{
	/// <summary>
	/// Summary description for Cryption.
	/// </summary>
	public sealed class Cryption
	{
		private RijndaelManaged Algorithm;
		private MemoryStream memStream;
		private ICryptoTransform EncryptorDecryptor;
		private CryptoStream crStream;
		private StreamWriter strWriter;
		private StreamReader strReader;

		private string m_key;
		private string m_iv;

		private byte[] key;
		private byte[] iv;

		private string pwd_str;
		private byte[] pwd_byte;

		public Cryption(string key_val, string iv_val)
		{
			key = new byte[32];
			iv = new byte[32];

			int i;
			m_key = key_val;
			m_iv = iv_val;

			for(i=0;i<m_key.Length;i++)
			{
				key[i] = Convert.ToByte(m_key[i]);
			}
			for(i=0;i<m_iv.Length;i++)
			{
				iv[i] = Convert.ToByte(m_iv[i]);
			}

		}

		public string Encrypt(string s)
		{
			Algorithm = new RijndaelManaged();

			Algorithm.BlockSize = 256;
			Algorithm.KeySize = 256;

			memStream = new MemoryStream();

			EncryptorDecryptor = Algorithm.CreateEncryptor(key,iv);

			crStream = new CryptoStream(memStream, EncryptorDecryptor, CryptoStreamMode.Write);

			strWriter = new StreamWriter(crStream);

			strWriter.Write(s);

			strWriter.Flush();
			crStream.FlushFinalBlock();

			pwd_byte = new byte[memStream.Length];
			memStream.Position = 0;
			memStream.Read(pwd_byte,0,(int)pwd_byte.Length);

			pwd_str = new UnicodeEncoding().GetString(pwd_byte);

			return pwd_str;
		}

		public string Decrypt(string s)
		{
			Algorithm = new RijndaelManaged();

			Algorithm.BlockSize = 256;
			Algorithm.KeySize = 256;

			MemoryStream memStream = new MemoryStream(new UnicodeEncoding().GetBytes(s));

			ICryptoTransform EncryptorDecryptor = Algorithm.CreateDecryptor(key,iv);
			memStream.Position = 0;
			CryptoStream crStream = new CryptoStream(memStream,EncryptorDecryptor,CryptoStreamMode.Read);
			strReader = new StreamReader(crStream);

			return strReader.ReadToEnd();
		}

        public static string Encrypt(string plainText, string passPhrase, string saltValue, string hashAlgorithm, 
            int passwordIterations, string initVector, int keySize)
        {
            // Convertim textul trimis ca parametru intr-un vector de tip byte.
            // Ne asiguram ca textul contine doar caractere de tip ASCII codes.
            // Urmeaza encodarea.
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            // Convertim textul intr-un vector de bytes.
            // Ne asiguram ca textul este UTF8
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            // Cream o parola de la care va deriva cheia de criptare
            // Aceasta parola va fi generata de la un anumit text prafrazat si o valoare de salt.
            // Parola va fi create folosind algoritmul de hash.
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                            passPhrase,
                                                            saltValueBytes,
                                                            hashAlgorithm,
                                                            passwordIterations);

            // Folosim parola pentru a genera un sir de pseudo-bytes pentru criptare.
            // Specificam marimea cheii in bytes.
            byte[] keyBytes = password.GetBytes(keySize / 8);

            // Cream un obiect neinitializat Rijndael pentru criptare.
            RijndaelManaged symmetricKey = new RijndaelManaged();

            // Acesta se va ocupa de criptare in modul CBC(Cipher Block Chaining)
            // Pentru cheia simetrica setam criptarea pe modul CBC
            symmetricKey.Mode = CipherMode.CBC;

            // Generam criptarea pornind de la cheia  de bytes existenta. 
            // Marimea cheii va fi definita in baza parametrului keyBytes
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                             keyBytes,
                                                             initVectorBytes);

            // Definim un memory stream pentru a aloca in el datele criptate.
            MemoryStream memoryStream = new MemoryStream();

            // Definim un stream cryptographic.
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                         encryptor,
                                                         CryptoStreamMode.Write);
            // Start criptare.
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

            // Stop criptare.
            cryptoStream.FlushFinalBlock();

            // Convertim datele criptate din stream intr-un vector de bytes.
            byte[] cipherTextBytes = memoryStream.ToArray();

            // Inchidem cele doua streamuri.
            memoryStream.Close();
            cryptoStream.Close();

            // Convertim datele criptate in tr-un string codatat base64.
            string cipherText = Convert.ToBase64String(cipherTextBytes);

            // Returnam stringul criptat.
            return cipherText;
        }

        public static string Decrypt(string cipherText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations,
                      string initVector, int keySize)
        {
            // Convertim stringul definit prin criptare intr-un vector de bytes si ne asiguram ca este in formatul ASCII.
            // Daca stringul contine caractere unicode folosim codul adecvat unicode, UTF7 sau UTF8.
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            // Convertim textul criptat intr-un vector de bytes.            
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

            // Cream o parola de la care va deriva cheia de criptare
            // Aceasta parola va fi generata de la un anumit text parafrazat si o valoare de salt. 
            // Parola va fi create folosind algoritmul de hash.
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                            passPhrase,
                                                            saltValueBytes,
                                                            hashAlgorithm,
                                                            passwordIterations);

            // Folosim parola pentru a genera un sir de pseudo-bytes pentru criptare.Specificam marimea cheii in bytes.
            byte[] keyBytes = password.GetBytes(keySize / 8);

            // Cream un obiect neinitializat Rijndael pentru criptare.
            RijndaelManaged symmetricKey = new RijndaelManaged();

            // Acesta se va ocupa de criptare in modul CBC(Cipher Block Chaining)
            // Pentru cheia simetrica setam criptarea pe modul CBC
            symmetricKey.Mode = CipherMode.CBC;

            // Generam un obiect decryptor pornind de la cheia existenta creata in vector
            // Marimea cheii va fi definita in baza parametrului keyBytes            
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
                                                             keyBytes,
                                                             initVectorBytes);

            // Definim un memory stream pentru a aloca in el datele criptate.
            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

            // Definim un stream cryptographic.
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                          decryptor,
                                                          CryptoStreamMode.Read);

            // Alocam vectorul de decriptare in functie de mariema vectorului de bytes de decriptat.
            byte[] plainTextBytes = new byte[cipherTextBytes.Length];

            // Start decriptare.
            int decryptedByteCount = cryptoStream.Read(plainTextBytes,
                                                       0,
                                                       plainTextBytes.Length);

            // Inchidem cele doua streamuri.
            memoryStream.Close();
            cryptoStream.Close();

            // Convertim textul decripat in string si ne asiguram daca este UTF8. 
            string plainText = Encoding.UTF8.GetString(plainTextBytes,
                                                       0,
                                                       decryptedByteCount);

            // Returnam stringul decriptat.     
            return plainText;
        }

        // Encrypt using DES
        public string EncryptDES(string plaintextString)
        {
            byte[] plaintextBytes = (new UnicodeEncoding()).GetBytes(plaintextString);
            
            SymmetricAlgorithm sa = DES.Create();
            MemoryStream msEncrypt = new MemoryStream();
            CryptoStream csEncrypt = new CryptoStream(msEncrypt, sa.CreateEncryptor(), CryptoStreamMode.Write);
            csEncrypt.Write(plaintextBytes, 0, plaintextBytes.Length);
            csEncrypt.Close();
            byte[] encryptedTextBytes = msEncrypt.ToArray();
            msEncrypt.Close();
            string cipherText = Convert.ToBase64String(encryptedTextBytes);

            return cipherText;
        }

        // Dencrypt using DES
        public string DecryptDES(string plaintextString)
        {
            SymmetricAlgorithm sa = DES.Create();
            
            byte[] cipherTextBytes = Convert.FromBase64String(plaintextString);

            MemoryStream msDecrypt = new MemoryStream(cipherTextBytes);
            CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(), CryptoStreamMode.Read);
            byte[] decryptedTextBytes = new Byte[cipherTextBytes.Length];
            byte[] plainTextBytes = new byte[cipherTextBytes.Length];

            int decryptedByteCount = csDecrypt.Read(plainTextBytes,
                                                       0,
                                                       plainTextBytes.Length);

            csDecrypt.Close();
            msDecrypt.Close();

            string plainText = Encoding.UTF8.GetString(plainTextBytes,
                                                       0,
                                                       decryptedByteCount);

            return plainText;
        }
	}
}